/**
 * @FilePath     : /14_imx6uirq/imx6uirq.c
 * @Description  :
 * @Author       : wxy 173055256627@163.com
 * @Version      : 0.0.1
 * @LastEditors  : wxy 173055256627@163.com
 * @LastEditTime : 2024-12-15 19:18:44
 * @Copyright    : G AUTOMOBILE RESEARCH INSTITUTE CO.,LTD Copyright (c) 2024.
 **/

#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/of_irq.h>
#include <linux/irq.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>

#define IMX6UIRQ_CNT 1
#define IMX6UIRQ_NAME "imx6uirq"
#define KEY_NUM 1
#define KEY0VALUE 0x01
#define INVAKEY 0xFF

typedef struct
{
    int gpio;
    int irqnum;
    unsigned char value;
    char name[10];
    irqreturn_t (*handler)(int, void *);
   
} irq_keydesc;

struct imx6uirq_dev
{
    dev_t devid;      // device
    struct cdev cdev; // character device
    struct class *class;
    struct device *device;
    struct timer_list timer;
    struct device_node *node;
    int major;
    int minor;
    irq_keydesc irqkey[KEY_NUM];

    atomic_t keyvalue;
    atomic_t key_releasekey;
};
static struct imx6uirq_dev imx6uirqdev;

static int imx6uirq_open(struct inode *inode, struct file *filp)
{
    filp->private_data = &imx6uirqdev;
    return 0;
}

static int imx6uirq_release(struct inode *inode, struct file *file)
{
    return 0;
}
/**
 * @brief        : 
 * @param         {file} *filp:
 * @param         {char __user} *buf:
 * @param         {size_t} size:
 * @param         {loff_t} *ppos:
 * @return        {*}
**/
static ssize_t imx6uirq_read(struct file *filp, char __user *buf, size_t size, loff_t *ppos)
{
    int ret = 0;
    unsigned char keyvalue;
    unsigned char releasekey;
    struct imx6uirq_dev *dev = filp->private_data;
    releasekey = atomic_read(&dev->key_releasekey);
    keyvalue = atomic_read(&dev->keyvalue);
    if (releasekey)
    {
        if (keyvalue & 0x80)
        {
            keyvalue &= ~0x80;
            ret = copy_to_user(buf, &keyvalue, sizeof(keyvalue));
        }
        else
        {
            goto data_error;
        }
        atomic_set(&dev->key_releasekey, 0);
    }else{
        goto data_error;
    }
    
    return ret;

data_error:
    return -EFAULT;
}

static struct file_operations imx6uirq_fops =
    {
        .owner = THIS_MODULE,
        .open = imx6uirq_open,
        .release = imx6uirq_release,
        .read = imx6uirq_read,
};

static irqreturn_t key0_handler(int irq, void *dev_id)
{
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)dev_id;
    printk("key0_handler is runing\n");

    // dev->timer.data = dev;
    dev->timer.data = (volatile long)&imx6uirqdev;
    mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));

    return IRQ_HANDLED;
}
static void timer_function(unsigned long data)
{
    int value = 0;
    struct imx6uirq_dev *dev = (struct imx6uirq_dev *)data;
    printk("timer_handler is runing\n");
    value = gpio_get_value(dev->irqkey[0].gpio);
    if (value == 0)
    {
        atomic_set(&dev->keyvalue, dev->irqkey[0].value);
        printk("key0 is pressed\n");
    }
    else if (value == 1)
    {
        atomic_set(&dev->keyvalue, (dev->irqkey[0].value) | 0x80);
        atomic_set(&dev->key_releasekey, 1);
        printk("key0 is released\n");
    }
}
/**
 * @brief        : keyio 初始化函数，里面有中断初始化
 * @param         {imx6uirq_dev} *dev:
 * @return        {*}
 **/
static int keyIO_init(struct imx6uirq_dev *dev)
{
    int ret = 0;
    int i = 0;
    printk("keyIO_init is runing\n");

    /* 1、按键初始化 */
    dev->node = of_find_node_by_path("/key"); // 为空表示错误
    if (dev->node == NULL)
    {
        ret = -EINVAL;
        goto fail_find_node;
    }
    for (i = 0; i < KEY_NUM; i++)
    {
        dev->irqkey[i].gpio = of_get_named_gpio(dev->node, "key-gpios", i);
    }
    for (i = 0; i < KEY_NUM; i++)
    {
        memset(dev->irqkey[i].name, 0, sizeof(dev->irqkey[i].name));
        sprintf(dev->irqkey[i].name, "key[%d]", i);
        gpio_request(dev->irqkey[i].gpio, dev->irqkey[i].name);
        gpio_direction_input(dev->irqkey[i].gpio);
        dev->irqkey[i].irqnum = gpio_to_irq(dev->irqkey[i].gpio);
    }
    /* 2、按键中断初始化 */
    dev->irqkey[0].handler = key0_handler;
    dev->irqkey[0].value = KEY0VALUE;
    // dev->irqkey[0].
    for (i = 0; i < KEY_NUM; i++)
    {
        // ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler, IRQF_TRIGGER_RISING  | IRQF_TRIGGER_FALLING, dev->irqkey[i].name, dev);
        ret = request_irq(dev->irqkey[i].irqnum, dev->irqkey[i].handler, IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, dev->irqkey[i].name, &imx6uirqdev);

        if (ret)
        {
            printk("request irq failed\n");
            goto fail_irq_request;
        }
    }
    /* 初始化定时器 */
    init_timer(&dev->timer);
    dev->timer.function = timer_function;
    return 0;
fail_irq_request:
    for (i = 0; i < KEY_NUM; i++)
    {
        gpio_free(dev->irqkey[i].gpio);
    }

fail_find_node:
    return ret;
}
static int __init imx6uirq_init(void)
{
    int ret = 0;
    printk("imx6uirq init\n");
    imx6uirqdev.major = 0;
    imx6uirqdev.minor = 0;
    if (imx6uirqdev.major)
    {
        imx6uirqdev.devid = MKDEV(imx6uirqdev.major, imx6uirqdev.minor);
        ret = register_chrdev_region(imx6uirqdev.devid, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
    }
    else
    {
        ret = alloc_chrdev_region(&imx6uirqdev.devid, 0, IMX6UIRQ_CNT, IMX6UIRQ_NAME);
        imx6uirqdev.major = MAJOR(imx6uirqdev.devid);
        imx6uirqdev.minor = MINOR(imx6uirqdev.devid);
    }
    if (ret < 0)
    {
        goto fail_devid;
    }
    printk("major=%d,minor=%d\r\n", imx6uirqdev.major, imx6uirqdev.minor);
    imx6uirqdev.cdev.owner = THIS_MODULE;
    cdev_init(&imx6uirqdev.cdev, &imx6uirq_fops);
    ret = cdev_add(&imx6uirqdev.cdev, imx6uirqdev.devid, IMX6UIRQ_CNT);
    if (ret < 0)
    {
        goto fail_cdev;
    }
    imx6uirqdev.class = class_create(THIS_MODULE, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirqdev.class))
    {
        ret = PTR_ERR(imx6uirqdev.class);
        ;
        goto fail_class;
    }
    imx6uirqdev.device = device_create(imx6uirqdev.class, NULL, imx6uirqdev.devid, NULL, IMX6UIRQ_NAME);
    if (IS_ERR(imx6uirqdev.device))
    {
        ret = PTR_ERR(imx6uirqdev.device);
        goto fail_device;
    }
    /* 初始化keyIO */
    ret = keyIO_init(&imx6uirqdev);
    if (ret)
    {
        goto fail_keyio_init;
    }
    /* 初始化原子变量 */
    atomic_set(&imx6uirqdev.keyvalue, 0);
    atomic_set(&imx6uirqdev.key_releasekey, 0);
    return 0;

fail_keyio_init:
    device_destroy(imx6uirqdev.class, imx6uirqdev.devid);
fail_device:
    class_destroy(imx6uirqdev.class);
fail_class:
    cdev_del(&imx6uirqdev.cdev);
fail_cdev:
    unregister_chrdev_region(imx6uirqdev.devid, IMX6UIRQ_CNT);
fail_devid:
    return ret;
}
static void __exit imx6irq_exit(void)
{
    int i = 0;
    printk("imx6uirq exit\n");
    del_timer(&imx6uirqdev.timer);
    for (i = 0; i < KEY_NUM; i++)
    {
        free_irq(imx6uirqdev.irqkey[i].irqnum, &imx6uirqdev);
    }
    for (i = 0; i < KEY_NUM; i++)
    {
        gpio_free(imx6uirqdev.irqkey[i].gpio);
    }
    cdev_del(&imx6uirqdev.cdev);
    unregister_chrdev_region(imx6uirqdev.devid, IMX6UIRQ_CNT);
    device_destroy(imx6uirqdev.class, imx6uirqdev.devid);
    class_destroy(imx6uirqdev.class);
}

module_init(imx6uirq_init);
module_exit(imx6irq_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("WXY");
